//
//  MujiMediaViewAssetResourceLoaderDelegate.m
//  ECCoreLibrary
//
//  Created by Paul Cantrell on 6/4/14.
//  Copyright (c) 2014 EnglishCentral, Inc. All rights reserved.
//

#import "MujiMediaViewAssetResourceLoaderDelegate.h"
#import <MobileCoreServices/MobileCoreServices.h>

@interface MujiMediaViewAssetResourceLoaderDelegate ()

@property (nonatomic,strong) MujiMediaLoadRequest *mediaLoadRequest;
@property (nonatomic,strong) NSMutableArray *pendingRequests;
@property (nonatomic,assign) BOOL downloadIsComplete;

@property (nonatomic,strong) NSString *hackedURL;
@property (nonatomic,strong) NSURLResponse *response;
@property (nonatomic,strong) NSString *mimeType;
@property (nonatomic,assign) BOOL downloadHasFinished;
@property (nonatomic,assign) int fileFD;
@property (nonatomic,assign) void *readBuffer;
@property (nonatomic,strong) NSString *filePath;
@property (nonatomic,assign) UInt64 fileLength;
@property (nonatomic,assign) UInt64 fileOffset;  // File offset as pertains to data given to connection
@property (nonatomic,assign) UInt64 readOffset;  // File offset as pertains to data read from file (but maybe not returned to connection)

@end

#define NULL_FD  -1
#define MAX_READ_LEN (32*1024)

@implementation MujiMediaViewAssetResourceLoaderDelegate

- (void)dealloc
{
    // This shouldn't happen, but just in case, be sure we don't leak file descriptors
    if( _fileFD >= 0 )
    {
        close(_fileFD);
    }
}

- (id)init
{
    self = [super init];
    if( self )
    {
        _pendingRequests = [[NSMutableArray alloc] init];
    }
    return self;
}

- (id)initWithMediaLoadRequest:(MujiMediaLoadRequest *)loadRequest
{
    self = [self init];
    if( self )
    {
        _mediaLoadRequest = loadRequest;
        _filePath = [_mediaLoadRequest localFilePath];
        _readBuffer = malloc(MAX_READ_LEN);
        _fileLength = 0;
        _fileOffset = 0;
        _fileFD = NULL_FD;
        [loadRequest addAsDelegate:self];
    }
    return self;
}

- (void)debug
{
    // NSLog(@"foo");
}

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
   //AVAssetResourceLoadingDataRequest *dataRequest = loadingRequest.dataRequest;
    // NSLog(@"DataRequest: %d bytes at %d offset currentOffset=%d (total = %d)",
    //          (int)dataRequest.requestedLength,
    //      (int)dataRequest.requestedOffset,
    //      (int)dataRequest.currentOffset,
    //      (int)dataRequest.requestedOffset+dataRequest.requestedLength);
    
    dispatch_async(_mediaLoadRequest.clientAccessQueue, ^(void){
        [_pendingRequests addObject:loadingRequest];
        [self processPendingRequests];
    });
    
    return YES;
}

- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
{
    dispatch_async(_mediaLoadRequest.clientAccessQueue, ^(void){
        [_pendingRequests removeObject:loadingRequest];
    });
}

- (void)processPendingRequests
{
    NSMutableArray *requestsCompleted = [NSMutableArray array];
    _fileLength = [_mediaLoadRequest availableContentLength];   // update how much is available

    for (AVAssetResourceLoadingRequest *loadingRequest in _pendingRequests)
    {
        [self fillInContentInformation:loadingRequest.contentInformationRequest];
        
        BOOL didRespondCompletely = [self respondWithDataForRequest:loadingRequest.dataRequest];
        
        if (didRespondCompletely)
        {
            [requestsCompleted addObject:loadingRequest];
            [loadingRequest finishLoading];
        }
        else if( [self bytesAvailableForDownload:loadingRequest.dataRequest] )
        {
            // NSLog(@"queued a restart request");
            [self performSelectorOnMainThread:@selector(restartPendingRequests)
                                   withObject:self
                                waitUntilDone:NO];
        }
        else
        {
            // NSLog(@"waiting for more data to arrive");
        }
    }
    
    [_pendingRequests removeObjectsInArray:requestsCompleted];
}

- (void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest *)contentInformationRequest
{
    if (contentInformationRequest == nil || _response == nil)
    {
        return;
    }
    
    NSString *mimeType = _mediaLoadRequest.response.MIMEType;
    CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(mimeType), NULL);
    
    contentInformationRequest.byteRangeAccessSupported = YES;
    contentInformationRequest.contentType = CFBridgingRelease(contentType);
    contentInformationRequest.contentLength = _mediaLoadRequest.contentLength;
}

- (NSUInteger)bytesAvailableForDownload:(AVAssetResourceLoadingDataRequest *)dataRequest
{
    long long startOffset = dataRequest.requestedOffset;
    if (dataRequest.currentOffset != 0)
    {
        startOffset = dataRequest.currentOffset;
    }
    if( _fileLength > startOffset )
    {
        long long lastRequestedByte = startOffset + dataRequest.requestedLength;
        long long lastAvailableByte = MIN(_fileLength, lastRequestedByte);
        long long numberOfBytesToRespondWith = lastAvailableByte - startOffset;
        long long numberOfBytesToRespondWithThatFitInBuffer = MIN(numberOfBytesToRespondWith, MAX_READ_LEN);
        return (NSUInteger)numberOfBytesToRespondWithThatFitInBuffer;
    }
    else
    {
        return 0;
    }
}

- (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingDataRequest *)dataRequest
{
    long long startOffset = dataRequest.requestedOffset;
    if (dataRequest.currentOffset != 0)
    {
        startOffset = dataRequest.currentOffset;
    }

    NSUInteger numberOfBytesToRespondWith = [self bytesAvailableForDownload:dataRequest];
    if( !numberOfBytesToRespondWith )
    {
        if( _downloadHasFinished )
        {
            return YES;
        }
        return NO;
    }
    
    if( _fileFD < 0 )
    {
        _fileFD = open( [_filePath UTF8String], O_RDONLY);
    }

    /*off_t seekResult = */lseek(_fileFD, (off_t)startOffset, SEEK_SET);
    ssize_t result = read(_fileFD, _readBuffer, (size_t)numberOfBytesToRespondWith);
    if( result == numberOfBytesToRespondWith )
    {
        NSData *dataReturn = [[NSData alloc] initWithBytes:_readBuffer length:numberOfBytesToRespondWith];
        [dataRequest respondWithData:dataReturn];
        _readOffset += [dataReturn length];
        long long endOffset = dataRequest.requestedOffset + dataRequest.requestedLength;
        BOOL didRespondFully = startOffset + dataReturn.length >= endOffset;
        
        // NSLog(@"Responded with %d bytes at offset %d %@",
        //      (int)numberOfBytesToRespondWith, (int)startOffset,
        //      didRespondFully ? @"true" : @"false");
        return didRespondFully;
    }
    else
    {
        if( result < 0 )
        {
           //    NSInteger error = errno;
            // NSLog(@"error %ld doing read",(long)error);
        }
        return YES;
    }
}

#pragma mark - Media Load Request Delegate Methods

- (void)newDataHasArrived
{
   if (_mediaLoadRequest.clientAccessQueue)
   {
      __weak typeof(&*self) wSelf = self;
      dispatch_async(_mediaLoadRequest.clientAccessQueue, ^(void){
         // NSLog(@"new data arrived");
         [wSelf processPendingRequests];
      });
   }
}

- (void)restartPendingRequests
{
   if (_mediaLoadRequest.clientAccessQueue)
   {
      __weak typeof(&*self) wSelf = self;
      dispatch_async(_mediaLoadRequest.clientAccessQueue, ^(void){
         [wSelf processPendingRequests];
      });
   }
}

- (void)downloadHasBegun
{
    // NSLog(@"download began...");
    _response = _mediaLoadRequest.response;
}

- (void)downloadHasCompleted
{
    _downloadHasFinished = YES;
   if (_mediaLoadRequest.clientAccessQueue)
   {
      __weak typeof(&*self) wSelf = self;
      dispatch_async(_mediaLoadRequest.clientAccessQueue, ^(void){
         [wSelf processPendingRequests];
      });
   }
}

- (void)downloadFailed:(MujiMediaLoadRequest *)mediaLoadRequest withError:(NSError *)error
{
   _downloadHasFinished = YES;
   if (_mediaLoadRequest.clientAccessQueue)
   {
      __weak typeof(&*self) wSelf = self;
      dispatch_async(_mediaLoadRequest.clientAccessQueue, ^
                     {
                        [wSelf processPendingRequests];
                     });
   }

}


@end
